package frame

import (
	"encoding/json"
	"gitee.com/jamon/espoir_go/common"
	"gitee.com/jamon/espoir_go/common/utils"
	"gitee.com/jamon/espoir_go/global"
	"gitee.com/jamon/espoir_go/logger"
	"gitee.com/jamon/espoir_go/route"
)

type Config struct {
	// 逻辑帧间隔，多少帧广播一次
	logicInterval uint8

	// 每秒多少帧
	frameRate uint8
}

func (c *Config) GetFrameInterval() int {
	return 1000 / int(c.frameRate)
}

type BattleDetail struct {
	users []*User
}

type SyncInfo struct {
	TargetFrameNo uint32          `json:"target_frame_no"`
	ActRecords    json.RawMessage `json:"act_records"`
}

func (s *SyncInfo) MarshalJSON() ([]byte, error) {
	return json.Marshal(map[string]interface{}{
		"target_frame_no": s.TargetFrameNo,
		"act_records":     s.ActRecords,
	})
}

type UserGameOp struct {
	UserID uint64 `json:"user_id"`
	Op     string `json:"op"`
}

type Server struct {
	id         string   // 每场战斗唯一标识
	userIDs    []uint64 // 参与战斗的玩家ID
	curFrameNo uint32   // 当前帧号

	cacheOp utils.Map[uint32, []*UserGameOp] // 帧号: 操作(encode_json)
	opLog   []string                         // 操作日志，用于回放(encode_json)
	Status  uint8                            // 0: 未开始，1: 进行中，2: 已结束

	detail *BattleDetail // 战斗详情
	config *global.Config

	routeCtr *route.Controller

	executor *common.SerializeController
}

func NewServer(id string) *Server {
	return &Server{
		id:         id,
		userIDs:    []uint64{},
		curFrameNo: 0,
		cacheOp:    utils.NewMap[uint32, []*UserGameOp](),
		opLog:      []string{},
		Status:     0,
		detail:     &BattleDetail{},
		config:     global.GetNodeData().Config,
		routeCtr:   route.GetController(),
		executor:   common.NewSerializeController(1024),
	}
}

func (s *Server) AddOp(userID uint64, jsonOp string) {
	s.executor.Execute(func() {
		ops, exist := s.cacheOp.Get(s.curFrameNo)
		if !exist {
			ops = []*UserGameOp{}
			s.cacheOp.Put(s.curFrameNo, ops)
		}
		ops = append(ops, &UserGameOp{
			UserID: userID,
			Op:     jsonOp,
		})
	}, "frame.Server.AddOp")
}

func (s *Server) processBroadcastInfo() (encodeStr []byte) {
	actJsonMsg, err := json.Marshal(s.cacheOp.M)
	s.cacheOp.Clear()
	encodeStr, err = json.Marshal(SyncInfo{
		TargetFrameNo: s.curFrameNo,
		ActRecords:    actJsonMsg,
	})
	if err != nil {
		logger.Error("GetBroadcastInfo", logger.Kwargs{"err": err})
		return
	}
	return
}

func (s *Server) AddUsers(userList []*User) {
	s.executor.Execute(func() {
		for _, user := range userList {
			s.userIDs = append(s.userIDs, user.UserID)
		}
		s.detail.users = userList
	}, "frame.Server.AddUsers")
}

func (s *Server) clear() {
	s.userIDs = []uint64{}
	s.detail = &BattleDetail{}
}

func (s *Server) Sync() {
	s.executor.Execute(func() {
		if s.Status != common.BattleStatus_RUNNING {
			return
		}

		if s.curFrameNo%uint32(s.config.Frame.LogicInterval) == 0 {
			s.broadcast()
		}
		s.curFrameNo++
	}, "frame.Server.Sync")
}

func (s *Server) broadcast() {
	var userIDs []uint64
	for _, user := range s.detail.users {
		userIDs = append(userIDs, user.UserID)
	}
	logger.Info("broadcast", logger.Kwargs{"user_ids": userIDs})
	encodeData := s.processBroadcastInfo()
	go common.SafeRun(func() {
		err := s.routeCtr.NarrowCastToClient(userIDs, common.DefaultInnerMsgIDs.FrameSyncMsgID, encodeData)
		if err != nil {
			return
		}
	})
}

func (s *Server) Stop() {
	s.executor.IsStop = true
	s.executor.Quit()
}
